using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.GAC;
using System.IO;
using System.Text;

namespace DebugCompanion.Engine
{
	static class FusionUtil
	{
		public static IEnumerable<AssemblyInfo> GetAssemblyFiles()
		{
			IAssemblyEnum e = AssemblyCache.CreateGACEnum();
			while (true)
			{
				IAssemblyName name;
				int errCode = e.GetNextAssembly(IntPtr.Zero, out name, 0);
				if (errCode != 0)
					break;

				yield return new AssemblyInfo(name);
			}
		}
	}

	class AssemblyInfo
	{
		public AssemblyInfo(IAssemblyName name)
		{
			_displayName = GetName(name, ASM_DISPLAY_FLAGS.VERSION | ASM_DISPLAY_FLAGS.PUBLIC_KEY_TOKEN);
			_publicKeyToken = GetPublicKeyToken(name);
			_version = AssemblyCache.GetVersion(name);
		}

		private string _displayName;

		public string DisplayName
		{
			[DebuggerStepThrough]
			get { return _displayName; }
		}

		public string Name
		{
			get
			{
				string res = Path.GetFileName(CodeBase);
				return res;
			}
		}

		static string GetName(IAssemblyName name, ASM_DISPLAY_FLAGS flags)
		{
			StringBuilder buf = new StringBuilder(1024);
			uint size = (uint)buf.Capacity;
			int errCode = name.GetDisplayName(buf, ref size, flags);
			if (errCode != 0)
				throw new Exception("errcode: " + errCode);

			string asmName = buf.ToString();
			return asmName;
		}

		private string _publicKeyToken;

		static string GetPublicKeyToken(IAssemblyName name)
		{
			byte[] tmp = AssemblyCache.GetPublicKeyToken(name);
			StringBuilder buf = new StringBuilder();
			foreach (byte cur in tmp)
			{
				buf.Append(cur.ToString("X2"));
			}
			return buf.ToString();
		}

		public string PublicKeyToken
		{
			[DebuggerStepThrough]
			get { return _publicKeyToken; }
		}

		private Version _version;

		public Version Version
		{
			[DebuggerStepThrough]
			get { return _version; }
		}

		public string CodeBase
		{
			get
			{
				IAssemblyCache cache = AssemblyCache.CreateAssemblyCache();
				ASSEMBLY_INFO aInfo = new ASSEMBLY_INFO();

				aInfo.cchBuf = 1024;
				aInfo.pszCurrentAssemblyPathBuf = new String('\0', (int)aInfo.cchBuf);
				cache.QueryAssemblyInfo(0, DisplayName, ref aInfo);

				string codeBase = aInfo.pszCurrentAssemblyPathBuf;
				return codeBase;
			}
		}

		public override string ToString()
		{
			return DisplayName;
		}
	}
}
